﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using Web7.Extensions;
using Web7.Utilities;

namespace Web7.Caching
{
    /// <summary>
    /// web7缓存Key构造
    /// </summary>
    [Serializable]
    public class Web7CacheKey : AbstractEquatableObject<Web7CacheKey>
    {
        public string ToJson()
        {
            return AddTypeToJson(GetType(), Serializer.ToJson(this));
        }

        public override string ToString()
        {
            return ToJson();
        }
        /// <summary>
        /// 扩展string
        /// </summary>
        public static implicit operator string(Web7CacheKey key)
        {
            return key.ToString();
        }

        protected string AsJson
        {
            get
            {
                return ToJson();
            }
        }
        /// <summary>
        /// 创建缓存Key
        /// </summary>
        /// <typeparam name="TReturn">返回类型</typeparam>
        /// <param name="from">key</param>
        /// <returns></returns>
        public static Web7CacheKey<TReturn> Create<TReturn>(TReturn from)
        {
            return new Web7CacheKey<TReturn>(@from);
        }
        /// <summary>
        /// 创建缓存key(用于扩展Key)
        /// </summary>
        /// <typeparam name="TKey">返回类型</typeparam>
        /// <param name="setup">方法</param>
        /// <returns></returns>
        public static Web7CacheKey<TKey> Create<TKey>(Action<TKey> setup)
            where TKey : class, new()
        {
            var keyOriginal = new TKey();
            setup.Invoke(keyOriginal);
            return new Web7CacheKey<TKey>(keyOriginal);
        }

        #region Overrides of AbstractEquatableObject<Web7CacheKey>

        /// <summary>
        /// 获取属性成员
        /// </summary>
        /// <returns></returns>
        /// <remarks></remarks>
        protected override IEnumerable<PropertyInfo> GetMembersForEqualityComparison()
        {
            yield return this.GetPropertyInfo(x => x.AsJson);
        }

        #endregion

        protected static string AddTypeToJson(Type type, string json)
        {
            if (!json.Contains("║"))
            {
                return type.FullName + "║" + json;
            }
            return json;
        }

        protected static string GetAndRemoveTypeFromJson(string json, out Type type)
        {
            if (json.Contains("║"))
            {
                var split = json.Split('║');
                if (split.Length != 2)
                {
                    type = null;
                    return json;
                }
                var typeName = split[0];
                var properJson = split[1];
                type = Type.GetType(typeName, false);
                return properJson;
            }
            type = null;
            return json;
        }
    }

    public class Web7CacheKey<T> : Web7CacheKey
    {
        public Web7CacheKey()
        {
        }

        public Web7CacheKey(T original)
        {
            Original = original;
        }

        public static readonly Web7CacheKey<T> Empty = default(Web7CacheKey<T>);

        public T Original { get; set; }

        public static implicit operator string(Web7CacheKey<T> key)
        {
            return key.ToString();
        }

        public static implicit operator Web7CacheKey<T>(string key)
        {
            Type realType = null;
            var getTypeFromJson = GetAndRemoveTypeFromJson(key, out realType);
            try
            {
                if (realType == typeof(Web7CacheKey<T>))
                    return Serializer.FromJson<Web7CacheKey<T>>(getTypeFromJson);
            }
            catch (SerializationException)
            {
                /* Don't log */
            }

            try
            {
                if (realType == typeof(T))
                {
                    var obj = Serializer.FromJson<T>(getTypeFromJson);
                    return new Web7CacheKey<T>(obj);
                }
            }
            catch (SerializationException)
            {
                /* Don't log */
            }

            return Web7CacheKey<T>.Empty;
        }

        protected override IEnumerable<PropertyInfo> GetMembersForEqualityComparison()
        {
            yield return this.GetPropertyInfo(x => x.Original);
        }
    }
}
